package my.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

/**
 * 类描述: 重写beanUtils,使得属性拷贝时能够忽略源对象中的空字段
 * 创建人: 闵楠  
 * 创建时间: 2016-7-8 下午02:27:47
 */
public abstract class BeanUtil extends BeanUtils {

	private static Logger log = Logger.getLogger(BeanUtil.class);
	
	/**
	 * 忽略掉源目标中的null属性
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void copyIgnoreNull(Object source, Object target) {
		try {
			Assert.notNull(source, "Source must not be null");
			Assert.notNull(target, "Target must not be null");
			
			Class<?> actualEditable = target.getClass();
			PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
			
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null) {
					PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						try {
							Method readMethod = sourcePd.getReadMethod();
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							
							// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时间格式转换等等  
							if (value != null) {
								Method writeMethod = targetPd.getWriteMethod();
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						} catch (Throwable ex) {
							throw new FatalBeanException("Could not copy properties from source to target", ex);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 忽略掉源目标中的null属性,并且自动进行类型转换
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	@SuppressWarnings("unchecked")
	public static void copyIgnoreNullAutoConvert(Object source, Object target, String[] convertNames, Class[] convertClasses) {
		try {
			Assert.notNull(source, "Source must not be null");
			Assert.notNull(target, "Target must not be null");
			
			Class<?> actualEditable = target.getClass();
			PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
			
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null) {
					PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						try {
							Method readMethod = sourcePd.getReadMethod();
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
//							Class sourceClass = sourcePd.getPropertyType();
							
							// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时间格式转换等等  
							if (value != null) {
								//自动转换类型
								if(convertNames != null && convertClasses != null) {
									if(convertNames.length == convertClasses.length) {
										if (convertNames.length > 0 && convertClasses.length > 0) {
											for (int i = 0; i < convertNames.length; i++) {
												String convertName = convertNames[i];
												Class convertClass = convertClasses[i];
												
												//如果是待转换的字段
												if(StringUtils.equals(sourcePd.getName(), convertName)) {
													try {
														value = convertClass.cast(value);
													} catch (Exception e) {
														try {
															if(!convertClass.equals(String.class)) {
																value = ConvertUtils.convert(value, convertClass);
															} 
															else if(convertClass.equals(Long.class)) {
																value = ConvertUtil.toLong(value);
															}
															else {
																throw new Exception();
															}
														} catch (Exception ex) {
															if(convertName.contains("_body") && convertClass.equals(String.class)) {
																value = JsonUtil.objToJson(value);
															} else if(convertName.contains("_body") && convertClass.equals(Map.class)) {
																value = JsonUtil.jsonToMap( (String)value );
															} else {
																value = JsonUtil.objToJson(value);
															}
														}
													}
												}
											}
										} 
									} else {
										log.debug("转换字段个数和转换类型个数不匹配");
									}
								} 
								
								Method writeMethod = targetPd.getWriteMethod();
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						} catch (Throwable ex) {
							throw new FatalBeanException("Could not copy properties from source to target", ex);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 忽略掉源目标中的null属性,并且自动进行类型转换
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	/*
	public static void copyIgnoreNullAutoConvertByMap(Object source, Object target, Map<String, Class> convertMap) {
		try {
			Assert.notNull(source, "Source must not be null");
			Assert.notNull(target, "Target must not be null");
			
			Class<?> actualEditable = target.getClass();
			PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
			
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null) {
					PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						try {
							Method readMethod = sourcePd.getReadMethod();
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							Class sourceClass = sourcePd.getPropertyType();
							
							// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时间格式转换等等  
							if (value != null) {
								//自动转换类型
								for(Entry<String, Class> entry : convertMap.entrySet()) {
									String convertName = entry.getKey();
									Class convertClass = entry.getValue();
									
									//如果是待转换的类型
									if(StringUtils.equals(sourcePd.getName(), convertName)) {
										try {
											value = convertClass.cast(value);
										} catch (Exception e) {
											value = JsonUtil.objToJson(value);
										}
									}
								}
								
								Method writeMethod = targetPd.getWriteMethod();
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						} catch (Throwable ex) {
							throw new FatalBeanException("Could not copy properties from source to target", ex);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	*/
	
	/**
	 * 忽略掉源目标中的null属性,并且将Object类型转成String
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	/*
	public static void copyIgnoreNullToDomain(Object source, Object target) {
		try {
			Assert.notNull(source, "Source must not be null");
			Assert.notNull(target, "Target must not be null");
			
			Class<?> actualEditable = target.getClass();
			PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
			
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null) {
					PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						try {
							Method readMethod = sourcePd.getReadMethod();
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							
							// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时间格式转换等等  
							if (value != null) {
								//如果源对象中有user_id属性,就从String类型转成Long
								if(source instanceof User && StringUtils.equals(sourcePd.getName(), "user_id") && value.getClass().equals(String.class)) {
									value = Long.valueOf( String.valueOf(value) );
								}
								
								//如果源对象中有Object属性,就转成String类型拷贝到目标对象
								if(value instanceof Map) {
									value = JsonUtil.objToJson(value);
								}
								
								Method writeMethod = targetPd.getWriteMethod();
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						} catch (Throwable ex) {
							throw new FatalBeanException("Could not copy properties from source to target", ex);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	*/
	
	/**
	 * 忽略掉源目标中的null属性,并且将String类型的body转成Object
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	/*
	public static void copyIgnoreNullToModel(Object source, Object target) {
		try {
			Assert.notNull(source, "Source must not be null");
			Assert.notNull(target, "Target must not be null");
			
			Class<?> actualEditable = target.getClass();
			PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
			
			for (PropertyDescriptor targetPd : targetPds) {
				if (targetPd.getWriteMethod() != null) {
					PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
					if (sourcePd != null && sourcePd.getReadMethod() != null) {
						try {
							Method readMethod = sourcePd.getReadMethod();
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							
							// 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时间格式转换等等  
							if (value != null) {
								//如果源对象中有_body属性,就从String类型转成Map
								if(sourcePd.getName().contains("_body") && value.getClass().equals(String.class)) {
									value = JsonUtil.jsonToMap( JsonUtil.objToJson(value) );
								}
								
								Method writeMethod = targetPd.getWriteMethod();
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}
						} catch (Throwable ex) {
							throw new FatalBeanException("Could not copy properties from source to target", ex);
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	*/
	
	/**
	 * 通过PropertiesUtil拷贝属性,失败时使用BeanUtils拷贝属性
	 * @param source
	 * @param target
	 * @throws BeansException
	 */
	public static void copyByPropertyUtils(Object source, Object target) {
		try {
			PropertyUtils.copyProperties(target, source);
		} catch (Exception e) {
			BeanUtils.copyProperties(source, target);
			e.printStackTrace();
		}
	}
	
	/**
	 * 生成对象
	 * @param <T>
	 * @param objClass
	 */
	public static <T> T newInstance(Class<T> objClass) {
		T t = null;
		
		try {
			t = objClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return t;
	}
	
	/**
	 * 根据实体筛选列表
	 * @param <T> 
	 * @param listObject 待筛选列表
	 * @param filter 筛选参数
	 * @return 筛选后的列表
	 */
	public static <T> List<T> filterList(List<T> listObject,T filter){
		ArrayList<T> result = new ArrayList<T>();
		Class<?> actualEditable = filter.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		for (T object : listObject) {
			boolean equal = true;
			for (PropertyDescriptor targetPd : targetPds) {
				Method readMethod = targetPd.getReadMethod();
				try {
					Object value1 = readMethod.invoke(filter);
					Object value2 = readMethod.invoke(object);
					if(!StringUtil.isNULL(value1)&&!value1.equals(value2)){
						equal = false;
						
						System.out.println("【不一样】"+value1+"："+value2);;
						break;
					}
				} catch (Exception e) {
					log.debug("获取参数值失败");
				} 
			}
			if(equal){
				result.add(object);
			}
		}
		return result;
	}
	/**
	 * 根据实体筛选列表
	 * @param <T> 
	 * @param listObject 待筛选列表
	 * @param filter 筛选参数
	 * @return 筛选后的列表
	 */
	public static <T> List<T> filterListByObject(List<T> listObject,Object filter){
		ArrayList<T> result = new ArrayList<T>();
		Class<?> actualEditable = filter.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		for (T object : listObject) {
			boolean equal = true;
			for (PropertyDescriptor targetPd : targetPds) {
				Method readMethod = targetPd.getReadMethod();//筛选对象get方法
				PropertyDescriptor sourcePd = getPropertyDescriptor(object.getClass(), targetPd.getName());
				if(sourcePd!=null&&!"class".equals(sourcePd.getName())){//被筛选对象有这个属性
					Method readMethod2 = sourcePd.getReadMethod();
					try {
						Object value1 = readMethod.invoke(filter);
						Object value2 = readMethod2.invoke(object);
						if(!StringUtil.isNULL(value1)&&!value1.equals(value2)){
							//筛选对象属性不为空，且筛选对象属性与被筛选对象属性不一致
							equal = false;
							break;
						}
					} catch (Exception e) {
						e.printStackTrace();
					} 
				}
			}
			if(equal){
				result.add(object);
			}
		}
		return result;
	}
	
	public static void test(Object filter){
		Class<?> actualEditable = filter.getClass();
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		for (PropertyDescriptor targetPd : targetPds) {
			String name = targetPd.getName();
			System.out.println(name);
		}
	}
	
}
